package com.tools.scanner.ui.ocr

import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.CornerPathEffect
import android.graphics.Matrix
import android.graphics.Paint
import android.graphics.Path
import android.graphics.PointF
import android.graphics.PorterDuff
import android.graphics.PorterDuffXfermode
import android.graphics.Rect
import android.graphics.RectF
import android.graphics.Typeface
import android.net.Uri
import android.os.Bundle
import android.text.Layout
import android.text.StaticLayout
import android.text.TextPaint
import android.text.TextUtils
import android.util.Log
import android.view.View
import androidx.lifecycle.lifecycleScope
import com.tools.scanner.BaseActivity
import com.tools.scanner.R
import com.tools.scanner.ad.AdManager
import com.tools.scanner.bean.OcrItemInfo
import com.tools.scanner.constant.AdLocationConstant
import com.tools.scanner.databinding.ActivityOcrPreviewBinding
import com.tools.scanner.ui.dialog.OcrTextDialog
import com.tools.scanner.ui.dialog.TranslateDialog
import com.tools.scanner.ui.dialog.TranslateInfo
import com.tools.scanner.utils.AppUtil
import com.tools.scanner.utils.DataReportUtil
import com.tools.scanner.utils.ImageUtil
import com.tools.scanner.utils.ResourceUtil
import com.tools.scanner.utils.TextOcrUtil
import com.tools.scanner.utils.ToastUtil
import com.tools.scanner.viewmodel.TranslatorViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.Locale
import kotlin.math.atan2
import kotlin.math.max

class OcrPreviewActivity : BaseActivity() {

    companion object {

        private const val IMAGE_URI = "image_uri"

        val LAN_CODE_MAP = mutableListOf<String>()

        init {
            LAN_CODE_MAP.add("auto")
            LAN_CODE_MAP.add("en")
            LAN_CODE_MAP.add("ja")
            LAN_CODE_MAP.add("ko")
            LAN_CODE_MAP.add("zh")
            LAN_CODE_MAP.add("es")
            LAN_CODE_MAP.add("ar")
            LAN_CODE_MAP.add("fr")
            LAN_CODE_MAP.add("de")
            LAN_CODE_MAP.add("ru")
            LAN_CODE_MAP.add("pt")
            LAN_CODE_MAP.add("hi")
            LAN_CODE_MAP.add("id")
            LAN_CODE_MAP.add("vi")
            LAN_CODE_MAP.add("it")
            LAN_CODE_MAP.add("nl")
            LAN_CODE_MAP.add("pl")
            LAN_CODE_MAP.add("hu")
            LAN_CODE_MAP.add("th")
            LAN_CODE_MAP.add("cs")
            LAN_CODE_MAP.add("fi")
            LAN_CODE_MAP.add("el")
            LAN_CODE_MAP.add("bg")
            LAN_CODE_MAP.add("bn")
            LAN_CODE_MAP.add("ca")
            LAN_CODE_MAP.add("et")
            LAN_CODE_MAP.add("ga")
            LAN_CODE_MAP.add("da")
            LAN_CODE_MAP.add("cy")
            LAN_CODE_MAP.add("be")
            LAN_CODE_MAP.add("fa")
            LAN_CODE_MAP.add("sv")
            LAN_CODE_MAP.add("uk")
        }

        fun actionStart(context: Context, uri: Uri) {
            val intent = Intent(context, OcrPreviewActivity::class.java)
            intent.putExtra(IMAGE_URI, uri)
            context.startActivity(intent)
        }
    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        outState.putParcelable(IMAGE_URI, mImageUri)
    }

    private lateinit var mBinding: ActivityOcrPreviewBinding
    private val mTranslatorViewModel = TranslatorViewModel()
    private val mTranslateMap = mutableMapOf<String, List<String?>>()
    private val mOcrAdLocation = AdLocationConstant.I_OCR
    private val mTranslatorAdLocation = AdLocationConstant.I_TRANSLATOR

    private var mSourceCode = ""
    private var mTargetCode = ""
    private var mImageUri: Uri? = null
    private var mOriginalBitmap: Bitmap? = null
    private var mOcrBitmap: Bitmap? = null
    private var mOcrItemInfoList: List<OcrItemInfo>? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mBinding = ActivityOcrPreviewBinding.inflate(layoutInflater)
        setContentView(mBinding.root)

        mImageUri = if (savedInstanceState != null) {
            savedInstanceState.getParcelable(IMAGE_URI)
        } else {
            intent.getParcelableExtra(IMAGE_URI)
        }

        val imageUri = mImageUri
        if (imageUri == null) {
            finish()
            return
        }

        if (savedInstanceState == null) {
            AdManager.loadInterstitialAd(mOcrAdLocation)
            AdManager.loadInterstitialAd(mTranslatorAdLocation)
        }

        AdManager.subscribeAdLoadStatusEvent(mTranslatorAdLocation, this) {
            if (it.status == AdManager.AdLoadStatus.SUCCESS) {
                mBinding.tvAdTips.visibility = View.VISIBLE
            }
        }

        AdManager.subscribeAdShowStatusEvent(mTranslatorAdLocation, this) {
            if (it.status == AdManager.AdShowStatus.DISMISSED) {
                hideLoadingDialog()
                drawTranslateBitmap()
            }
        }

        AdManager.subscribeAdShowStatusEvent(mOcrAdLocation, this) {
            if (it.status == AdManager.AdShowStatus.DISMISSED) {
                hideLoadingDialog()
                drawOcrBitmap()
            }
        }

        mTranslatorViewModel.translatorLiveData.observe(this) { translatorInfo ->
            val ocrItemInfoList = mOcrItemInfoList
            if (translatorInfo == null || ocrItemInfoList == null) {
                hideLoadingDialog()
                return@observe
            }

            val key = getTranslateBufferKey()
            mTranslateMap[key] = translatorInfo.dataList

            var index = 0
            translatorInfo.dataList.forEach { translateText ->
                val itemInfo = ocrItemInfoList[index]

                try {
                    itemInfo.translateText = translateText
                    itemInfo.isTranslate = true
                } catch (e: Exception) {
                    e.printStackTrace()
                }

                index++
            }

            mBinding.tvTranslateTips.setText(R.string.original_text)
            mBinding.llTranslate.tag = true
            showTranslatorAd()
        }

        mBinding.llBack.setOnClickListener {
            finish()
        }

        mBinding.llText.setOnClickListener {
            mOcrItemInfoList?.let { list ->
                OcrTextDialog(this, list).show()
            }
        }

        mBinding.tvSourceCode.setOnClickListener {
            showTranslateDialog(mSourceCode, true)
        }

        mBinding.tvTargetCode.setOnClickListener {
            showTranslateDialog(mTargetCode, false)
        }

        mBinding.llTranslate.setOnClickListener {
            if (it.tag == true) {
                mBinding.tvTranslateTips.setText(R.string.translate)
                mBinding.llTranslate.tag = false
                mOcrItemInfoList?.forEach { item ->
                    item.isTranslate = false
                }
                drawOcrBitmap()
            } else {
                translate()
            }
        }

        initSourceCode()
        initTargetCode()

        lifecycleScope.launch(Dispatchers.Main) {
            showLoadingDialog()

            withContext(Dispatchers.IO) {
                ImageUtil.uriToBitmap(this@OcrPreviewActivity, imageUri)?.let {
                    mOriginalBitmap = it
                }
            }

            mBinding.imageView.setImageBitmap(mOriginalBitmap)
            performOcr(imageUri)
        }
    }

    private fun initSourceCode() {
        val osLang = LAN_CODE_MAP[0]
        val resourceName = "translate_${osLang}"
        mBinding.tvSourceCode.text = ResourceUtil.getString(this, resourceName)
        mSourceCode = osLang
    }

    private fun initTargetCode() {
        val osLang = AppUtil.getOSLang().toLowerCase(Locale.getDefault())
        val finalOsLang = if (LAN_CODE_MAP.contains(osLang)) {
            osLang
        } else {
            LAN_CODE_MAP[1]
        }

        val resourceName = "translate_${finalOsLang}"
        mBinding.tvTargetCode.text = ResourceUtil.getString(this, resourceName)
        mTargetCode = finalOsLang
    }

    private fun showOcrAd() {
        AdManager.addStepCount(mOcrAdLocation)

        if (!AdManager.isLoadComplete(mOcrAdLocation)) {
            hideLoadingDialog()
            drawOcrBitmap()
            AdManager.loadInterstitialAd(mOcrAdLocation)
            return
        }

        AdManager.showInterstitialAd(mOcrAdLocation, this)
    }

    private fun showTranslatorAd() {
        AdManager.addStepCount(mTranslatorAdLocation)

        if (!AdManager.isLoadComplete(mTranslatorAdLocation)) {
            hideLoadingDialog()
            AdManager.loadInterstitialAd(mTranslatorAdLocation)
            drawTranslateBitmap()
            AppUtil.rateUs(this)
            return
        }

        AdManager.showInterstitialAd(mTranslatorAdLocation, this)
    }

    private fun performOcr(uri: Uri) {
        TextOcrUtil.performOcr(this, uri, { result ->
            mOcrItemInfoList = result
            showOcrAd()
        }, { e ->
            hideLoadingDialog()
            ToastUtil.showToast(R.string.analyzer_fail)
            DataReportUtil.reportOcrFail(e.toString())
        })
    }

    private fun drawOcrBitmap() {
        mOcrBitmap?.let {
            mBinding.imageView.setImageBitmap(it)
            return
        }

        val originalBitmap = mOriginalBitmap ?: return
        val ocrItemInfoList = mOcrItemInfoList ?: return

        val mutableBitmap = originalBitmap.copy(Bitmap.Config.ARGB_8888, true)

        val canvas = Canvas(mutableBitmap)
        canvas.drawBitmap(originalBitmap, 0f, 0f, null)

        val overlayBitmap = Bitmap.createBitmap(
            originalBitmap.width,
            originalBitmap.height,
            Bitmap.Config.ARGB_8888
        )
        val overlayCanvas = Canvas(overlayBitmap).apply {
            drawColor(Color.parseColor("#80000000"))
        }

        val eraserPaint = Paint().apply {
            xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
            isAntiAlias = true
            pathEffect = CornerPathEffect(resources.getDimension(R.dimen.dp_5))
        }

        ocrItemInfoList.forEach { textBlock ->
            textBlock.lines.forEach { line ->
                line.cornerPoints?.let { points ->
                    Path().apply {
                        moveTo(points[0].x.toFloat(), points[0].y.toFloat())
                        points.drop(1).forEach { point ->
                            lineTo(point.x.toFloat(), point.y.toFloat())
                        }
                        close()
                    }.also { path ->
                        val bounds = RectF()
                        path.computeBounds(bounds, true)
                        val centerX = bounds.centerX()
                        val centerY = bounds.centerY()
                        val matrix = Matrix().apply {
                            postScale(1.1f, 1.1f, centerX, centerY)
                        }
                        path.transform(matrix)

                        overlayCanvas.drawPath(path, eraserPaint)
                    }
                }
            }
        }

        canvas.drawBitmap(overlayBitmap, 0f, 0f, null)

        mOcrBitmap = mutableBitmap
        mBinding.imageView.setImageBitmap(mutableBitmap)
    }

    private fun drawTranslateBitmap() {
        val originalBitmap = mOriginalBitmap ?: return
        val ocrItemInfoList = mOcrItemInfoList ?: return

        val resultBitmap = originalBitmap.copy(Bitmap.Config.ARGB_8888, true)
        val canvas = Canvas(resultBitmap)

        canvas.drawColor(Color.parseColor("#80000000"))

        val mosaicPaint = Paint().apply {
            isAntiAlias = false
            isFilterBitmap = false
            xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_OVER)
        }

        val edgePaint = Paint().apply {
            color = Color.TRANSPARENT
            style = Paint.Style.STROKE
            strokeWidth = 2f
            pathEffect = CornerPathEffect(8f)
            isAntiAlias = true
        }

        val textPaint = TextPaint()
        textPaint.color = Color.RED
        textPaint.typeface = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL)
        if ("ja" == mTargetCode) {
            textPaint.isSubpixelText = true
            textPaint.isLinearText = true
        }

        ocrItemInfoList.forEach { textBlock ->
            val cornerPoints = textBlock.cornerPoints
            val translatedText = textBlock.translateText?.replace("\n", "")
            if (cornerPoints != null && translatedText != null && cornerPoints.size >= 4) {
                val path = Path().apply {
                    moveTo(cornerPoints[0].x.toFloat(), cornerPoints[0].y.toFloat())
                    (1 until cornerPoints.size).forEach { i ->
                        lineTo(cornerPoints[i].x.toFloat(), cornerPoints[i].y.toFloat())
                    }
                    close()
                }

                var bounds = RectF().apply { path.computeBounds(this, true) }
                val centerX = bounds.centerX()
                val centerY = bounds.centerY()
                val matrix = Matrix().apply {
                    postScale(1.1f, 1.1f, centerX, centerY)
                }
                path.transform(matrix)

                bounds = RectF().apply { path.computeBounds(this, true) }

                if (bounds.width() > 0 && bounds.height() > 0) {
                    val mosaicStrength =
                        (max(bounds.width(), bounds.height()) / 20).toInt().coerceAtLeast(8)

                    val blurred = originalBitmap
                        .getSafeSubBitmap(bounds)
                        .createBlurredRegion(this, mosaicStrength)

                    canvas.save()
                    canvas.clipPath(path)
                    canvas.drawBitmap(blurred, null, bounds, mosaicPaint)
                    canvas.drawPath(path, edgePaint)
                    canvas.restore()

                    val startPoint =
                        PointF(cornerPoints[0].x.toFloat(), cornerPoints[0].y.toFloat())
                    val endPoint = PointF(cornerPoints[1].x.toFloat(), cornerPoints[1].y.toFloat())
                    val rotationAngle = calculateRotationAngle(startPoint, endPoint)

                    val aaaa = Matrix().apply {
                        postRotate(-rotationAngle)
                    }
                    path.transform(aaaa)
                    bounds = RectF().apply { path.computeBounds(this, true) }

                    textPaint.textSize =
                        getTextSizeToFitRect(bounds, translatedText, textPaint, rotationAngle)
                    val layout = StaticLayout.Builder
                        .obtain(
                            translatedText,
                            0,
                            translatedText.length,
                            textPaint,
                            bounds.width().toInt()
                        )
                        .setAlignment(Layout.Alignment.ALIGN_NORMAL)
                        .setLineSpacing(0f, 1f)
                        .setIncludePad(true)
                        .build()

                    val centerX = bounds.centerX()
                    val centerY = bounds.centerY()

                    canvas.save()
                    canvas.rotate(rotationAngle)
                    canvas.translate(centerX, centerY)
                    canvas.translate(-layout.width / 2f, -layout.height / 2f)
                    layout.draw(canvas)

                    canvas.restore()
                }
            }
        }

        mBinding.imageView.setImageBitmap(resultBitmap)
    }

    private fun Bitmap.getSafeSubBitmap(area: RectF): Bitmap {
        val safeArea = Rect(
            (area.left - 5).toInt().coerceAtLeast(0),
            (area.top - 5).toInt().coerceAtLeast(0),
            (area.right + 5).toInt().coerceAtMost(width),
            (area.bottom + 5).toInt().coerceAtMost(height)
        )
        return if (safeArea.width() > 0 && safeArea.height() > 0) {
            Bitmap.createBitmap(
                this,
                safeArea.left,
                safeArea.top,
                safeArea.width(),
                safeArea.height()
            )
        } else {
            Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
        }
    }

    private fun Bitmap.createBlurredRegion(context: Context, radius: Int): Bitmap {
        val smallBitmap = Bitmap.createScaledBitmap(
            this,
            max(1, width / 4),  // 先缩小提升性能
            max(1, height / 4),
            false
        )

        return Test.fastblur(smallBitmap, 30)
    }

    private fun calculateRotationAngle(start: PointF, end: PointF): Float {
        val dx = end.x - start.x
        val dy = end.y - start.y
        return Math.toDegrees(atan2(dy.toDouble(), dx.toDouble())).toFloat()
    }

    private fun calculateRotatedBounds(original: RectF, degrees: Float): RectF {
        val matrix = Matrix().apply {
            postRotate(degrees, original.centerX(), original.centerY())
        }
        val rotated = RectF(original)
        matrix.mapRect(rotated)
        return rotated
    }

    private fun getTextSizeToFitRect(
        rect: RectF,
        text: String,
        paint: Paint,
        rotationAngle: Float,
        tolerance: Float = 0.9f
    ): Float {
        // 计算目标面积
        val targetArea = (rect.width() * rect.height()) * tolerance
        val textLength = text.length

        // 初始化字体大小
        var estimatedTextSize = 1f // 可以从1开始，避免除以零的错误

        // 使用 Paint 对象测量文本
        paint.textSize = estimatedTextSize
        var textWidth = paint.measureText(text)
        var textHeight = paint.fontMetrics.descent - paint.fontMetrics.ascent

        // 迭代调整字体大小，直到文本适合矩形
        while (textWidth * textHeight < targetArea) {
            estimatedTextSize += 1f // 或者使用更小的增量
            paint.textSize = estimatedTextSize
            textWidth = paint.measureText(text)
            textHeight = paint.fontMetrics.descent - paint.fontMetrics.ascent
        }

        // 返回最终估算的字体大小
        return estimatedTextSize
    }

    private fun showTranslateDialog(code: String, isNeedAuto: Boolean) {
        TranslateDialog(this, code, isNeedAuto) { name, code ->
            if (isNeedAuto) {
                mBinding.tvSourceCode.text = name
                mSourceCode = code
            } else {
                mBinding.tvTargetCode.text = name
                mTargetCode = code
            }

            translate()
        }.show()
    }

    private fun translate() {
        val sourceCode = mSourceCode
        val targetCode = mTargetCode
        val ocrItemInfoList = mOcrItemInfoList
        if (TextUtils.isEmpty(sourceCode) || TextUtils.isEmpty(targetCode) || ocrItemInfoList == null) {
            return
        }

        val key = getTranslateBufferKey()
        val bufferList = mTranslateMap[key]
        if (!bufferList.isNullOrEmpty()) {
            hideLoadingDialog()
            mOcrItemInfoList?.forEachIndexed { index, ocrItemInfo ->
                ocrItemInfo.isTranslate = true
                ocrItemInfo.translateText = bufferList[index]
            }
            mBinding.tvTranslateTips.setText(R.string.original_text)
            mBinding.llTranslate.tag = true
            drawTranslateBitmap()
            return
        }

        val textList = mutableListOf<String>()
        ocrItemInfoList.forEach { itemInfo ->
            textList.add(itemInfo.originalText)
        }

        showLoadingDialog()
        mTranslatorViewModel.translator(sourceCode, targetCode, textList)
    }

    private fun getTranslateBufferKey(): String {
        return mSourceCode + "_" + mTargetCode
    }
}

















